#ifndef CALCULATOR_H
#define CALCULATOR_H

#include <iostream>
#include <stdexcept>
#include "LinkedList.h"
#include "Token.h"

using namespace std;

class Calculator
{
public:
	enum Rank { NUMERAL, BRACKET_START, ADDITION, MULTIPLICATION, BRACKET_END };

	Rank SymbolRank(Token token)
	{
		if(token.isOperator())
		{
			char symbol = ((Operator)token).get();

			if(symbol == ')' || symbol == ']' || symbol == '}')
				return BRACKET_END;
			else if(symbol == '*' || symbol == '/')
				return MULTIPLICATION;
			else if(symbol == '+' || symbol == '-')
				return ADDITION;
			else if(symbol == '(' || symbol == '[' || symbol == '{')
				return BRACKET_START;
		}
		else
			return NUMERAL;
	}

	Token OppositeBracket(Token token)
	{
		if(token.isOperator())
		{
			char in = ((Operator)token).get();
			switch(in)
			{
			case '(':	return Operator(')');
			case '[':	return Operator(']');
			case '{':	return Operator('}');
			case ')':	return Operator('(');
			case ']':	return Operator(']');
			case '}':	return Operator('}');
			}
		}
		return NULL;
	}

	LinkedList<Token> Infix2Postfix(LinkedList<Token> exp)
	{
		LinkedList<Token> stack;
		LinkedList<Token> result;
		for(int i=0; i < exp.Length(); i++)
		{
			if(SymbolRank(exp.Get(i)) == NUMERAL)
				result.PushBack(exp.Get(i));
			else if(SymbolRank(exp.Get(i)) == BRACKET_START)
				stack.PushBack(exp.Get(i));
			else if(SymbolRank(exp.Get(i)) == ADDITION)
			{
				while(stack.Length() > 0 && SymbolRank(stack.Get(stack.Length()-1)) >= ADDITION)
					result.PushBack(stack.PopBack());
				stack.PushBack(exp.Get(i));
			}
			else if(SymbolRank(exp.Get(i)) == MULTIPLICATION)
			{
				for(int j=stack.Length()-1; SymbolRank(stack.Get(j)) >= MULTIPLICATION && stack.Length()>0; j--)
					result.PushBack(stack.PopBack());
				stack.PushBack(exp.Get(i));
			}
			else if(SymbolRank(exp.Get(i)) == BRACKET_END)
			{
				try
				{
					for(int j=stack.Length()-1; stack.Get(j) != OppositeBracket(exp.Get(i)); j--)
						result.PushBack(stack.PopBack());
					stack.PopBack();
				}
				catch(std::out_of_range e)
				{	throw std::out_of_range("Unbalanced statement.");	}
			}
			cout << "Expression: ";
			for(int j=0; j<exp.Length(); j++)
				cout << exp.Get(j);
			cout << endl << "Stack: ";
			for(int j=0; j<stack.Length(); j++)
				cout << stack.Get(j) << " ";
			cout << endl << "Result: ";
			for(int j=0; j<result.Length(); j++)
				cout << result.Get(j);
			cout << endl << endl;
		}
		while(stack.Length() > 0)
			result.PushBack(stack.PopBack());
		return result;
	}

	double Evaluate(LinkedList<Token> input)
	{
		LinkedList<Token> output;
		for(int i=0; i<input.Length(); i++)
		{
			if(input.Get(i).isOperand())
				output.PushBack(input.Get(i));
			else
			{
				Operand operand2 = output.PopBack();
				Operand operand1 = output.PopBack();
				Operand result = Operand(((Operator)input.Get(i)).operate(operand1, operand2));
				output.PushBack(result);
			}
		}
		return ((Operand)output.PopBack()).get();
	}
};

#endif CALCULATOR_H